package main

import (
	"net"
	"bufio"
	"encoding/json"
	"time"
	"fmt"
)

/*
	TO DO : each channel msg chan cache is 20 , Is it too small ?
		no use HandlePing

*/

type Channel struct {
	token string    //userid + sequence number
	conn *net.TCPConn
	msg chan *Msg
	reader  *bufio.Reader
	writer *bufio.Writer
	close chan int
	rooms *LinkList
}

func NewChannel(token string,conn *net.TCPConn) *Channel{
	c := &Channel{}
	c.msg = make(chan *Msg,20)
	c.close = make(chan int)
	c.conn = conn
	c.token = token
	c.reader = bufio.NewReader(conn)
	c.writer = bufio.NewWriter(conn)
	c.rooms = NewLinkList()
	return c
}

func (c *Channel)PushMsg(m *Msg){
	c.msg <- m
}
func (c *Channel)Close(){
	c.close <- 1
}
func (c *Channel)HandleMsg(){
	for{
		var closec int
		select {
		case message := <- c.msg:
			p := NewPushProto()
			mJson,err := json.Marshal(message)

			if err != nil{
				continue
			}
			p.SetData(mJson)
			p.WriteTcp(c.writer)
		case  cl := <- c.close:
			closec = cl
			break
		}
		if closec == 1{
			break
		}

	}
}



func (c *Channel)HandlePing(){
	for{
		p := NewPingProto()
		p.SetData([]byte("heartbeat"))
		p,err := p.WriteTcp(c.writer)
		if err != nil{
			break
		}
		time.Sleep(10*time.Second)
	}

	fmt.Println("Ping error close Channel")
	c.Close()
	c.conn.Close()
}


func ProcessCmd(p *Proto,writer *bufio.Writer,conn *net.TCPConn) (*Channel,error){
	switch p.Cmd {
	case AUTH:
		reply := &Proto{Ver:1,Cmd:AUTH_REPLY}
		channel,token := AddChannel(string(p.Header),conn)
		reply.SetData([]byte(token))
		reply.SetHeader([]byte{})
		if _,err := reply.WriteTcp(writer);err != nil{
			fmt.Println("AUTH write fail =>",err)
			return nil,err
		}
		return channel,nil

	case HEARTBEAT:{
		//reply := &Proto{Ver:1,Cmd:HEARTBEAT_REPLY}
		//reply.SetData([]byte("HEARTBEAT_REPLY"))
		//reply.SetHeader([]byte{})
		//if _,err := reply.WriteTcp(writer);err != nil{
		//	fmt.Println("HEARTBEAT_REPLY fail =>",err)
		//	return nil,err
		//}
		return nil,nil
	}
	case MSG_REPLY:{
		return nil,nil
	}
	case ROOM_MSG_REPLY:{
		return nil,nil
	}
	case JOIN_ROOM:{
		reply := &Proto{Ver:1,Cmd:JOIN_ROOM_REPLY}
		channel,err := GetChannelByToken(string(p.Header))
		if err != nil{
			reply.SetData([]byte(" Join Room Key is not exist"))
		}else{
			JoinRoom(channel,string(p.Data))
			reply.SetData([]byte(fmt.Sprintf("Join Room %s success",string(p.Data))))
		}
		if _,err := reply.WriteTcp(writer);err != nil{
			fmt.Println("Join Room write fail =>",err)
			return nil,err
		}
		return nil,nil
	}
	case LEAVE_ROOM:{
		reply := &Proto{Ver:1,Cmd:LEAVE_ROOM_REPLY}
		channel,err := GetChannelByToken(string(p.Header))
		if err != nil{
			reply.SetData([]byte(" Leave Room Key is not exist"))
		}else{
			if err := LeaveRoom(channel,string(p.Data));err != nil{
				reply.SetData([]byte(fmt.Sprintf("Leave Room %s Error,user not join the room or no exist the room",string(p.Data))))
			}else{
				reply.SetData([]byte(fmt.Sprintf("Leave Room %s success",string(p.Data))))
			}


		}
		if _,err := reply.WriteTcp(writer);err != nil{
			fmt.Println("Leave Room fail =>",err)
			return nil,err
		}
		return nil,nil
	}
	default:
		fmt.Println("ProcessCmd() default :", p)
		return nil,nil
	}

}






